#pragma execution_character_set("utf-8")

#include "savelog.h"
#include <QMutex>
#include <QDateTime>
#include <QGlobalStatic>
#include <QFile>
#include <QApplication>
#include <QTextStream>
#include "common.h"

Q_GLOBAL_STATIC(SaveLog,m_instance)

//日志重定向
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
void Log(QtMsgType type, const QMessageLogContext &context, const QString &msg)
#else
void Log(QtMsgType type, const char *msg)
#endif
{
    //加锁,防止多线程中qdebug太频繁导致崩溃
    static QMutex mutex;
    QMutexLocker locker(&mutex);
    QString content;
    QString msgColor;

    QString dateTime = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
    //这里可以根据不同的类型加上不同的头部用于区分
    int msgType = SaveLogIns->GetMsgType();
    switch (type) {
        case QtDebugMsg:
            if ((msgType & MsgType_Debug) == MsgType_Debug) {
                content = QString("当前时间：%1 %2").arg(dateTime).arg(msg);
                msgColor = "rgb(10, 152, 228)";
            }
            break;
#if (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
        case QtInfoMsg:
            if ((msgType & MsgType_Info) == MsgType_Info) {
                content = QString("当前时间：%1 %2").arg(dateTime).arg(msg);
                msgColor = "rgb(34,139,34)";
            }
            break;
#endif
        case QtWarningMsg:
            if ((msgType & MsgType_Warning) == MsgType_Warning) {
                content = QString("当前时间：%1 %2").arg(dateTime).arg(msg);
                msgColor = "rgb(252, 175, 62)";
            }
            break;
        case QtCriticalMsg:
            if ((msgType & MsgType_Critical) == MsgType_Critical) {
                content = QString("当前时间：%1 %2").arg(dateTime).arg(msg);
                msgColor = "rgb(250, 48, 48)";
            }
            break;
        case QtFatalMsg:
            if ((msgType & MsgType_Fatal) == MsgType_Fatal) {
                content = QString("Fatal 当前时间：%1 %2").arg(dateTime).arg(msg);
                msgColor = "rgb(124, 24, 35)";
            }
            break;
    }    

    //没有内容则返回
    if (content.isEmpty()) {
        return;
    }

    //加上打印代码所在代码文件、行号、函数名
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
    if (SaveLogIns->GetUseContext()) {
        int line = context.line;
        QString file = context.file;
        QString function = context.function;
        if (line > 0) {
            content = QString("行号: %1  文件: %2  函数: %3\n%4").arg(line).arg(file).arg(function).arg(content);
        }
    }
#endif

    //还可以将数据转成html内容分颜色区分
    QString color_content = "<span style='color:%1;'>%2</span>";
    QString out_mag = color_content.arg(msgColor).arg(content);

    emit SaveLogIns->flushLogUI(out_mag);

    //将内容传给函数进行处理
    SaveLogIns->Save(content);
}

SaveLog::SaveLog(QObject *parent) : QObject(parent)
{
    isRun = false;
    maxRow = currentRow = 0;
    maxSize = 1024;
    useContext = false;

    //全局文件对象
    file = new QFile(this);
    //默认取应用的根目录
    path = qApp->applicationDirPath();
    //默认取应用程序可执行文件名称
    QString str = qApp->applicationFilePath();
    QStringList list = str.split("/");
    name = list.at(list.count() - 1).split(".").at(0);
    fileName = "";

    //默认所有类型都输出
    msgType = MsgType(MsgType_Debug | MsgType_Info | MsgType_Warning | MsgType_Critical | MsgType_Fatal);
}

SaveLog::~SaveLog()
{
    this->Stop();
}

SaveLog *SaveLog::Instance()
{
    return m_instance;
}

void SaveLog::openFile(const QString &fileName)
{
    //当文件名改变的时候新建和打开文件，或者一开始就打开
    if(this->fileName != fileName)
    {
        this->fileName = fileName;
        //关闭之前的
        if(file->isOpen())
        {
            file->close();
        }
        //设置新的日志文件
        file->setFileName(fileName);
        //以append的方式追加
        file->open(QIODevice::WriteOnly | QIODevice::Append | QFile::Text);
    }
}

bool SaveLog::GetUseContext()
{
    return this->useContext;
}

MsgType SaveLog::GetMsgType()
{
    return this->msgType;
}

void SaveLog::Start()
{
    if(isRun)
    {
        return;
    }

    isRun = true;

#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
    qInstallMessageHandler(Log);
#else
    qInstallMsgHandler(Log);
#endif
}

void SaveLog::Stop()
{
    if(!isRun)
    {
        return;
    }

    isRun = false;
    this->Clear();

#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
    qInstallMessageHandler(0);
#else
    qInstallMsgHandler(0);
#endif
}

void SaveLog::Clear()
{
    currentRow = 0;
    fileName.clear();
    if(file->isOpen())
    {
        file->close();
    }
}

void SaveLog::Save(const QString &content)
{
    //日志存储规则多种策略 优先级 行数》大小》日期
    //1：设置了最大行数限制则按照行数限制来
    //2：设置了大小则按照大小来控制日志文件
    //3：都没有设置都存储到日期命名的文件，只有当日期发生变化才会切换到新的日志文件
    bool needOpen = false;
    if(maxRow > 0)
    {
        currentRow++;
        if(fileName.isEmpty())
        {
            needOpen = true;
        }
        else if(currentRow >= maxRow)
        {
            needOpen = true;
        }
    }
    else if(maxSize > 0)
    {
        //1MB = 1024*1024
        //首次需要重新打开文件以及超过大小需要重新打开文件
        if(fileName.isEmpty())
        {
            needOpen = true;
        }
        else if(file->size() > (maxSize * 1024))
        {
            needOpen = true;
        }
    }
    else
    {
        //日期发生改变才会触发
        QString fileName = QString("%1/%2_log_%3.log").arg(path).arg(name).arg(QDATE);
        openFile(fileName);
    }

    if((maxRow > 0 || maxSize > 0) && needOpen)
    {
        currentRow = 0;
        QString fileName = QString("%1/%2_log_%3.log").arg(path).arg(name).arg(STRDATETIME);
        openFile(fileName);
    }

    //用文本流输出速度更快
    QTextStream stream(file);
    stream << content << NEWLINE;
}

void SaveLog::SetMaxRow(int maxRow)
{
    //这里可以限定最大最小值
    if (maxRow >= 0)
    {
        this->maxRow = maxRow;
        this->Clear();
    }
}

void SaveLog::SetMaxSize(int maxSize)
{
    //限定最大最小值
    if(maxSize >= 0)
    {
        this->maxSize = maxSize;
        this->Clear();
    }
}

void SaveLog::SetUseContext(bool useContext)
{
    this->useContext = useContext;
}

void SaveLog::SetPath(const QString &path)
{
    this->path = path;
}

void SaveLog::SetName(const QString &name)
{
    this->name = name;
}

void SaveLog::SetMsgType(const MsgType &msgType)
{
    this->msgType = msgType;
}
